import { GpEcsWorld } from "../GpEcsWorld";
import { Comp } from "../CompDef";

import { Sglt } from "../SgltDefine";
import { ObjPool } from "../../../common/ObjPool";
import { QuadTree } from "../../../common/QuadTree";
import { MathUtils, Vect3 } from "../../../common/MathUtils";
import ecs, { EcsComponent, EcsEntity, EcsEntityGroup} from "ecs-ts";

import { EHeroAttri, OwnSkill } from "../GpDef";
import { Vec2 } from "cc";
import { Grid, AStarFinder, DiagonalMovement, Util } from "pathfinding";
// import { AStarFinder, DiagonalMovement, Grid, Util } from "../../../../node_modules";
export class CommonSystem extends ecs.EcsSystem<GpEcsWorld> {
    uiInfo: Sglt.UiInfo
    worldInfo: Sglt.WorldInfo
    removedIndex = 0
    grid: Grid


    pfGroup: EcsEntityGroup = null
    heroGroup: EcsEntityGroup = null
    distroyTimerGroup: EcsEntityGroup = null

    // finder = new AStarFinder({
    //     diagonalMovement: DiagonalMovement.OnlyWhenNoObstacles
    // });
    public quadTree: QuadTree<EcsEntity>

    quadTreePoll = new ObjPool<QuadTree<EcsEntity>>(QuadTree)
    constructor(world: GpEcsWorld, defaltGroupComps: (new () => EcsComponent)[] = [Comp.Transform, Comp.Force]) {
        super(world, defaltGroupComps);
        this.pfGroup = this.world.getGroupByClz(Comp.Transform, Comp.PfNode)
        this.heroGroup = this.world.getGroupByClz(Comp.Transform, Comp.Hero)
        this.distroyTimerGroup = this.world.getGroupByClz(Comp.Common.DistroyTimer)


        this.uiInfo = this.world.getSingleton(Sglt.UiInfo)
        this.worldInfo = this.world.getSingleton(Sglt.WorldInfo)

        // this.grid = new Grid(this.worldInfo.width / this.worldInfo.pfNodeWidth, this.worldInfo.height / this.worldInfo.pfNodeWidth)


    }

    update(): void {
        this.updataDistroyTimerGroup()
        this.updateQuadTree()
        // this.updatePfGrid()

        if (this.world.frameCount % 30 == 0) {
            if (this.heroGroup.entities.length < 50) {
                for (let index = 0; index < 1; index++) {
                    // this.addHero(this.world.random.inRollPercent(80) ? 1 : 0)
                    this.addHero(1)
                }
            }

            // if (this.defaultGroup.entities.length > 1000) {
            //     if (this.removedIndex >= this.defaultGroup.entities.length) {
            //         this.removedIndex = 0
            //     }
            //     const entity = this.defaultGroup.entities[this.removedIndex]
            //     if (entity.component(Comp.Force).value == 1) {
            //         this.world.removeEntity(entity.id)
            //     }
            //     this.removedIndex++
            // }
        }


    }

    /**更新distroy定时器分组 */
    updataDistroyTimerGroup() {
        this.distroyTimerGroup.entities.forEach((entity) => {
            const comp = entity.component(Comp.Common.DistroyTimer)
            if (comp.createTimeMs + comp.durationMs < this.world.elapsedTimeMs) {
                this.world.removeEntity(entity.id)
            }
        })
    }

    gridValidator = (node, grid: Grid) => {
        const x = node.x
        const y = node.y
        const intersectRadius = grid.objRadius
        const intersectXMin = Math.max(0, x - intersectRadius + 1)
        const intersectXMax = Math.min(x + intersectRadius - 1, this.worldInfo.pfGridWidth - 1)
        const intersectYMin = Math.max(0, y - intersectRadius + 1)
        const intersectYMax = Math.min(y + intersectRadius - 1, this.worldInfo.pfGridHeight - 1)
        for (let i = intersectXMin; i <= intersectXMax; i++) {
            for (let j = intersectYMin; j <= intersectYMax; j++) {
                if (grid.nodes[j][i].walkable === false) {
                    return false
                }
            }
        }


        return true
    }

    /**在grid中找到一个距离指定位置最近的一个满足条件的位置 */
    findNearestGridNode(nodes: any[][], targetRow: number, targetCol: number, condition: Function): Vect3 | null {
        const rows = nodes.length;
        const cols = nodes[0].length;
        const visited = new Array(rows).fill(false).map(() => new Array(cols).fill(false));

        const queue: [number, number, number][] = [[targetRow, targetCol, 0]];

        while (queue.length > 0) {
            const [row, col, distance] = queue.shift()!;

            if (row < 0 || row >= rows || col < 0 || col >= cols || visited[row][col]) {
                continue;
            }

            visited[row][col] = true;

            if (condition(row, col)) {
                return new Vect3(row, col);
            }

            queue.push([row - 1, col, distance + 1]);
            queue.push([row + 1, col, distance + 1]);
            queue.push([row, col - 1, distance + 1]);
            queue.push([row, col + 1, distance + 1]);
        }

        return null;
    }
    /**距离目标位置最近的一个可移动的点 */
    getNearestWalkablePos(transform: Comp.Transform, targetPos: Vect3) {
        const x = Math.floor(targetPos.x / this.worldInfo.pfNodeWidth) + this.worldInfo.pfGridWidth / 2
        const y = Math.floor(targetPos.y / this.worldInfo.pfNodeWidth) + this.worldInfo.pfGridHeight / 2
        this.grid.objRadius = Math.ceil((transform.radius + (this.worldInfo.pfNodeWidth / 2)) / this.worldInfo.pfNodeWidth)

        return this.findNearestGridNode(this.grid.nodes, x, y, (x, y) => {
            if (!this.grid.isInside(x, y)) {
                return false
            }

            const node = this.grid.getNodeAt(x, y)
            if (!this.gridValidator(node, this.grid)) {
                return false
            }
            const worldPos = this.worldInfo.gridPosToWorldPos(x, y)

            const distanceFromSelf = Vect3.distance(worldPos, transform.pos)
            const distanceFromTarget = Vect3.distance(worldPos, targetPos)
            return distanceFromTarget <= distanceFromSelf
        })
    }

    /**遍历grid在指定圆形范围内上所有的点 */
    forEachGridInCircle(grid: Grid, center: Vect3, radius: number, cb: (x: number, y: number) => void) {
        const x = Math.floor(center.x / this.worldInfo.pfNodeWidth) + this.worldInfo.pfGridWidth / 2
        const y = Math.floor(center.y / this.worldInfo.pfNodeWidth) + this.worldInfo.pfGridHeight / 2
        const radiusGrid = Math.ceil((radius + (this.worldInfo.pfNodeWidth / 2)) / this.worldInfo.pfNodeWidth)
        for (let i = -radiusGrid; i <= radiusGrid; i++) {
            for (let j = -radiusGrid; j <= radiusGrid; j++) {
                const targetX = x + i
                const targetY = y + j
                if (!this.grid.isInside(targetX, targetY)) {
                    continue
                }
                const distance = MathUtils.distance(x, y, targetX, targetY)
                if (distance < radiusGrid && grid.isInside(targetX, targetY)) {
                    cb(targetX, targetY)
                }
            }
        }
    }

    /**刷新pathfinderGrid */
    updatePfGrid() {
        this.grid.nodes.forEach((row) => {
            row.forEach((node) => {
                node.walkable = true
            })
        })

        this.pfGroup.entities.forEach((entity) => {
            const transform = entity.component(Comp.Transform)
            this.forEachGridInCircle(this.grid, transform.pos, transform.radius, (x, y) => {
                this.grid.setWalkableAt(x, y, false)
            })
        })
    }

    findPath(entity: EcsEntity, startPos: Vect3, endPos: Vect3, radian = 0) {
        const grid = this.grid.clone() as any

        // this.defaultGroup.entities.forEach((e) => {
        //     if (e.id != entity.id) {
        //         const transform = e.component(Comp.Transform)
        //         this.forEachGridInCircle(grid, transform.pos, transform.radius, (x, y) => {
        //             grid.setWalkableAt(x, y, false)
        //         })
        //     }

        // })
        grid.validator = this.gridValidator
        grid.objRadius = Math.ceil((radian + (this.worldInfo.pfNodeWidth / 2)) / this.worldInfo.pfNodeWidth)
        // const startGridPos = this.worldInfo.worldPosToGridPos(startPos.x, startPos.y)
        // const endGridPos = this.worldInfo.worldPosToGridPos(endPos.x, endPos.y)
        const startGridPos = startPos
        const endGridPos = endPos

        const startNode = this.grid.getNodeAt(startGridPos.x, startGridPos.y)
        const endNode = this.grid.getNodeAt(endGridPos.x, endGridPos.y)

        if (!this.gridValidator(endNode, grid)) {
            return []
        }

        // const path = this.finder.findPath(startNode.x, startNode.y, endNode.x, endNode.y, grid)
        // return Util.compressPath(path)
    }

    /**创建一个四叉树 */
    makeQuadTree(quadTreePoll) {
        return quadTreePoll.get().setBoundary(-this.worldInfo.width / 2, -this.worldInfo.height / 2, this.worldInfo.width, this.worldInfo.height)
    }
    updateQuadTree() {
        this.quadTree = this.makeQuadTree(this.quadTreePoll)
        for (let i = 0; i < this.defaultGroup.entities.length; i++) {
            const entity = this.defaultGroup.entities[i];

            const transform = entity.componentMap.get(0) as Comp.Transform

            this.quadTree.insert(transform.pos as any as Vec2, entity, this.quadTreePoll)
        }
    }
    temp = false


    protected onFirstUpdate(): void {
        this.addHero(0, (entity) => {
            const transform = entity.component(Comp.Transform)
            const battleInfo = entity.component(Comp.Battle.BattleInfo)
            const worldInfo = this.worldInfo
            transform.pos.x = 0
            transform.pos.y = 0 + 1000 //worldInfo.height / 2
            transform.moveSpeed = 5
            // transform.rotateSpeed = 0.1
            transform.radius = 80
            transform.height = 350
            transform.width = 60

            battleInfo.curHp = 10000000000
            Comp.Battle.Attri.modify(entity.component(Comp.Battle.Attri), EHeroAttri.hp, battleInfo.curHp)
            Comp.Battle.Attri.modify(entity.component(Comp.Battle.Attri), EHeroAttri.atp, 6666)

            battleInfo.findEnemyRange = 2000
            battleInfo.attRange = 100
            battleInfo.isMelee = true
            Comp.Battle.Attri.modify(entity.component(Comp.Battle.Attri), EHeroAttri.attSpeed, 1)

            battleInfo.bulletSpeed = 1000
            battleInfo.enabelAutoAtt = true

            entity.component(Comp.Battle.OwnSkills).skills = [new OwnSkill(1), new OwnSkill(3), new OwnSkill(4)]
            Comp.Battle.OwnSkills.sortSkillByPriority(entity.component(Comp.Battle.OwnSkills))
        })
    }



    /**添加战斗单位 */
    addHero(forceId: number = 0, cb: (entity: EcsEntity) => void = null) {
        const entity = this.world.addEntityWithComps(
            [
                Comp.Transform,
                Comp.Hero,
                Comp.Force,
                Comp.Move,
                Comp.Battle.BattleInfo,
                Comp.HeroAction,
                Comp.Battle.OwnSkills,
                Comp.Battle.BuffContainer,
                Comp.Battle.Attri,
                Comp.Battle.Alive
            ],
            (e) => {
                e.component(Comp.Force).value = forceId

                const battleAttri = e.component(Comp.Battle.Attri)
                e.component(Comp.Battle.BattleInfo).curHp = this.world.random.randomIntInclusive(20000, 50000)
                Comp.Battle.Attri.modify(e.component(Comp.Battle.Attri), EHeroAttri.hp, e.component(Comp.Battle.BattleInfo).curHp)

                // e.component(Comp.Battle.BattleInfo).hp = e.component(Comp.Battle.BattleInfo).curHp = this.world.random.randomIntInclusive(20000, 50000)


                const transform = e.component(Comp.Transform)
                const battleInfo = e.component(Comp.Battle.BattleInfo)
                transform.moveSpeed = this.world.random.randomIntInclusive(1, 3)
                const createPosOffset = 800
                const createPos = this.world.random.randomElementFromList([new Vect3(-createPosOffset, -createPosOffset, 0), new Vect3(createPosOffset, createPosOffset, 0), new Vect3(-createPosOffset, createPosOffset, 0), new Vect3(createPosOffset, -createPosOffset, 0)])
                // transform.pos.x = createPos.x
                // transform.pos.y = createPos.y

                transform.pos.x = this.worldInfo.width * 0.8 * this.world.random.random() - this.worldInfo.width * 0.4
                transform.pos.y = (this.world.random.random() * this.worldInfo.height * -0.4)+1000

                // transform.pos.x = createPos.x + MathUtils.fixNumber(this.world.random.randomFloat(-1, 1))
                // transform.pos.y = createPos.y + MathUtils.fixNumber(this.world.random.randomFloat(-1, 1))
                const scale = 1//this.world.random.randomFloat(0.6, 1)

                transform.radius = Math.floor(50 * scale)
                transform.height = Math.floor(350 * scale)
                transform.width = Math.floor(50 * scale)

                Comp.Battle.Attri.modify(e.component(Comp.Battle.Attri), EHeroAttri.atp, transform.radius)


                // transform.pos.x = MathUtils.fixNumber(this.world.random.randomFloat(-500, 500))
                // transform.pos.y = MathUtils.fixNumber(this.world.random.randomFloat(-600, 600))

                // transform.pos.x = MathUtils.fixNumber(this.world.random.randomFloat(-200, 200))
                // transform.pos.y = this.world.random.randomElementFromList([1, -1]) * 200

                // entity.component(Comp.Transform).rotateSpeed = MathUtils.fixNumber(this.world.random.randomFloat(-0.1, 0.1))
                battleInfo.findEnemyRange = 2000
                battleInfo.isMelee = this.world.random.inRollPercent(50) ? true : false
                battleInfo.attRange = battleInfo.isMelee ? transform.radius * 2 : transform.radius * 8
                battleInfo.enabelAutoAtt = true
                Comp.Battle.Attri.modify(e.component(Comp.Battle.Attri), EHeroAttri.attSpeed, 1)


                e.component(Comp.Battle.OwnSkills).skills = [new OwnSkill(2)]

                if (cb) {
                    cb(e)
                }
            }
        )

        return entity
    }
}

